﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WorkFlowCore.Conditions;
using WorkFlowCore.Framework.Migrations;

namespace WorkFlowCore.Framework.Conditions
{
    [Condition("Json条件处理器", "可用于解析流程主表单和审批步骤的表单。支持 !=,>=,<=,<,>,==,⊆,∈,&&,|| 等表达式，表达式支持通过括号 隔开。如果需要解析审批步骤的表单数据，需要在表达式前面加上 \"step:\"。表达式支持解析json表单的数据，使用  ${jsonkey} 可获取json表单里的值进行判断")]
    public class JsonCondition : ICondition
    {
        public bool CanAccept(ConditionInput input)
        {
            //var expression = "step:(${days}>10&&${days}<20)||${days}==30&&(${days}>50)".Replace(" ","");
            //var jsonData = "{ days: 10}";

            var expression = "";
            var jsonData = "";

            //这里规定，step: 开头标识要解析的是审批步骤填写的表单数据
            if (input.Expression.StartsWith("step:"))
            {
                expression = input.Expression.Substring(5);
                jsonData = input.CurrentWorkStep.FormData;
            }
            else
            {
                expression = input.Expression ?? "";
                jsonData = input.WorkTask.FormData;
            }

            return Analyze(expression, jsonData);
        }

        private bool Analyze(string expression, string jsonData)
        {
            var stack = new Stack<char>();
            var stackIndex = new Stack<int>();

            for (int i = 0; i < expression.Length; i++)
            {
                var c = expression[i];

                if (c == '(')
                {
                    stack.Push(c);
                    stackIndex.Push(stack.Count - 1);
                }
                else if (c == ')')
                {
                    var startIndex = stackIndex.Pop();
                    string exp = GetStackValue(stack, startIndex);
                    stack.Pop();
                    //TODO 再搞一个栈处理bool 数据

                    //bool 处理完回来应该是返回 true、false，这里用 1、0 代替，重新入栈
                    stack.Push(OrExpressionHandler(exp, jsonData) ? '1' : '0');
                }
                else
                {
                    stack.Push(c);
                }
            }
            return OrExpressionHandler(GetStackValue(stack, -1), jsonData);
        }

        private static string GetStackValue(Stack<char> stack, int startIndex)
        {
            var charList = new List<char>();
            while (stack.Count > startIndex + 1)
            {
                charList.Add(stack.Pop());
            }
            charList.Reverse();
            var exp = string.Join("", charList);
            return exp;
        }

        private bool  OrExpressionHandler(string exp, string jsonData)
        {
            var exps = exp.Split(new string[] { "||" }, StringSplitOptions.None);

            foreach (var expItem in exps)
            {
                if(AndExpressionHandler(expItem, jsonData))return true;
            }
            return false;
        }

        private bool AndExpressionHandler(string exp, string jsonData)
        {
            var exps = exp.Split(new string[] { "&&" }, StringSplitOptions.None);

            foreach (var expItem in exps)
            {
                if(!CompareValue(expItem, jsonData))return false;
            }
            return true;
        }

        private VType GetValue<VType>(string expression, string formData)
        {
            //被 value() 方法包含时，需要解析，否则原值返回
            var regex = new Regex(@"\$\{(?<value>.*)\}");

            if (regex.IsMatch(expression))

            {
                JObject jObject = JObject.Parse(formData);
                var token = jObject.SelectToken(regex.Match(expression).Groups["value"].Value);
                return token.Value<VType>();
            }
            else return (VType)Convert.ChangeType(expression, typeof(VType));
        }
        private bool CompareValue(string exp,string jsonData)
        {
            try
            {
                //简单的表达式解析

                var queryExpressions = exp.Split(new string[] { "&&" }, StringSplitOptions.None);
                var regexy = new Regex("(?<key>.*)(?<operation>!=|>=|<=|<|>|==|⊆|∈)(?<value>.*)");

                var paramList = new List<object>();

                foreach (var expression in queryExpressions)
                {
                    if (!regexy.IsMatch(expression))
                    {
                        if (expression == "0") return false;
                        else if (expression == "1") return true;
                        else continue;
                    }
                    var matchs = regexy.Match(expression);
                    var operation = matchs.Groups["operation"].Value;

                    var key = matchs.Groups["key"].Value.Trim();
                    var keyValue = GetValue<string>(key, jsonData);//
                    var value = matchs.Groups["value"].Value.Trim();
                    var valueValue = GetValue<string>(value, jsonData);//
                    var result = false;

                    switch (operation)
                    {
                        case "!=":
                            result = !keyValue.Equals(valueValue);
                            break;
                        case ">=":
                            result = decimal.Parse(keyValue) >= decimal.Parse(valueValue);
                            break;
                        case "<=":
                            result = decimal.Parse(keyValue) <= decimal.Parse(valueValue);
                            break;
                        case ">":
                            result = decimal.Parse(keyValue) > decimal.Parse(valueValue);
                            break;
                        case "<":
                            result = decimal.Parse(keyValue) < decimal.Parse(valueValue);
                            break;
                        case "==":
                            result = keyValue.Equals(valueValue);
                            break;
                        case "⊆"://包含
                            result = keyValue.Contains(valueValue);
                            break;
                        case "∈"://属于
                            result = valueValue.Contains(keyValue);
                            break;
                        default:
                            break;
                    }
                    if (!result) return false;
                }
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
